AWS Lambda(C#)でCognitoの認証情報を取得する方法
はじめに
CognitoをオーソライザーにしたAPI Gatewayの認証情報をLambdaの中で取得する方法を調べました。言語はC#で.NET Coreのバージョンは3.1を使っています。
手順
- Cognitoのユーザプールとユーザーを作成してください。ユーザーとアプリクライアントを1件登録しておいてください。
- 下のソースコードでLambdaファンクションを作成してください。
- API Gatewayを使ってAPIを作成します。作成したLambdaファンクションを呼び出します。「プロキシ統合の使用」にチェックを入れてください。
- API Gatewayのオーソライザーは作成したCognitoのユーザープールを設定します。トークンのソースは今回は「Authorization」にしました。
- 認証情報を取得できているか確認します。私はテストアプリを作りました。
参照しているパッケージ
Lambdaのパッケージは以下を参照しています。
- Amazon.Lambda.APIGatewayEvents 2.4.0
- Amazon.Lambda.Core 1.2.0
- Amazon.Lambda.Serialization.SystemTextJson 2.1.0
- AWSSDK.APIGateway 3.5.2.15
- AWSSDK.Lambda 3.5.5.3
Lambdaのソースコード
以下、Lambdaのソースコードになります。内容はHTTPヘッダーにあるIDトークンを解析して、Cognitoに登録したメールアドレスを返しています。
using Amazon.Lambda.Core; using System; using System.Net; using System.Text.Json; using System.Text.Json.Serialization; using Amazon.Lambda.APIGatewayEvents; using System.Text; [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace CognitoTest { public class Function { public APIGatewayProxyResponse FunctionHandler(Request request, ILambdaContext context) { var idToken = request.Headers.GetProperty("Authorization").GetString(); var section = idToken.Split('.')[1]; var padded = section.PadRight(section.Length + (section.Length * 3) % 4, '='); var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(padded)); string email = ""; using (JsonDocument document = JsonDocument.Parse(decoded)) { JsonElement root = document.RootElement; email = root.GetProperty("email").GetString(); } return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = email }; } } public class Request { [JsonPropertyName("body")] public string Body { get; set; } [JsonPropertyName("httpMethod")] public string HttpMethod { get; set; } [JsonPropertyName("headers")] public JsonElement Headers { get; set; } [JsonPropertyName("requestContext")] public JsonElement RequestContext { get; set; } } }
追記
後で気づいたのですが、メールアドレスやCognitoユーザーIDの取得であれば、requestContextというプロパティに入っていました。以下のようにしてメールアドレスを取得できます。HTTPヘッダーからIDトークンを取得するよりも、こちらの方が簡単でいいですね。
var authorizer = request.RequestContext.GetProperty("authorizer"); var claims = authorizer.GetProperty("claims"); var mail = claims.GetProperty("email").GetString();
確認する
私はUWPで簡単なテストアプリを作って確認しました。画面側はボタン1つだけの画面なので割愛します。やっていることはCognitoへのログインとAPIの呼び出しです。Cognitoにログインした際に取得したIDトークンをAPI呼び出し時にHTTPヘッダーに入れています。パッケージは以下をダウンロードしました。
- Amazon.Extensions.CognitoAuthentication.2.0.0
- AWSSDK.CognitoIdentity.3.5.1.9
- AWSSDK.CognitoIdentityProvider.3.5.1.7
using Amazon; using Amazon.CognitoIdentityProvider; using Amazon.Extensions.CognitoAuthentication; using System; using System.Diagnostics; using System.Net.Http; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace CongnitoTest { public sealed partial class MainPage : Page { // お使いの環境の情報を設定してください。 private const string POOL_ID = "プールID"; private const string CLIENT_ID = "クライアントID"; private const string USERNAME = "ユーザー名"; private const string PASSWORD = "パスワード"; private const string URI = "URI"; public MainPage() { this.InitializeComponent(); } private async void Button_Click(object sender, RoutedEventArgs e) { // 認証 var provider = new AmazonCognitoIdentityProviderClient(null, RegionEndpoint.APNortheast1); CognitoUserPool userPool = new CognitoUserPool( POOL_ID, CLIENT_ID, provider ); CognitoUser user = new CognitoUser( USERNAME, CLIENT_ID, userPool, provider ); AuthFlowResponse response = await user.StartWithSrpAuthAsync(new InitiateSrpAuthRequest() { Password = PASSWORD }).ConfigureAwait(false); // API var httpRequest = new HttpRequestMessage(HttpMethod.Post, new Uri(URI)); string idToken = response.AuthenticationResult.IdToken; httpRequest.Headers.Add("Authorization", idToken); using (var client = new HttpClient()) { var res = await client.SendAsync(httpRequest); if (res.StatusCode.Equals(System.Net.HttpStatusCode.OK)) { Debug.WriteLine("success:" + await res.Content.ReadAsStringAsync()); } else { Debug.WriteLine("failure"); } } } } }